#include "SQLDeleteStatement.h"

#include "SQLExprTableSource.h"
#include "SQLTableSource.h"
#include "../SQLName.h"
#include "../SQLObject.h"
#include "../../../DbType.h"
#include "../../SQLUtils.h"
#include "../expr/SQLBinaryOpExpr.h"
#include "../expr/SQLBinaryOpExprGroup.h"
#include "../expr/SQLBinaryOperator.h"
#include "../expr/SQLIdentifierExpr.h"

#include "../../visitor/SQLASTVisitor.h"
#include "../../../utils/instanceof.h"
#include "SQLSubqueryTableSource.h"
#include "SQLWithSubqueryClause.h"

SQLDeleteStatement::SQLDeleteStatement()
{
}

SQLDeleteStatement::SQLDeleteStatement(DbType_ptr dbType) : SQLStatementImpl(dbType)
{
}

void SQLDeleteStatement::cloneTo(SQLDeleteStatement_ptr x)
{
  if (!headHints->empty())
  {
    for (SQLCommentHint_ptr h : *headHints)
    {
      SQLCommentHint_ptr h2 = std::dynamic_pointer_cast<SQLCommentHint>(h->clone());
      h2->SQLObjectImpl::setParent(x);
      x->headHints->push_back(h2);
    }
  }

  if (with != NULL)
  {
    x->setWith(std::dynamic_pointer_cast<SQLWithSubqueryClause>(with->clone()));
  }

  if (tableSource != NULL)
  {
    x->setTableSource(std::dynamic_pointer_cast<SQLTableSource>(tableSource->clone()));
  }
  if (where != NULL)
  {
    x->setWhere(std::dynamic_pointer_cast<SQLExpr>(where->clone()));
  }
  if (from != NULL)
  {
    x->setFrom(std::dynamic_pointer_cast<SQLTableSource>(from->clone()));
  }
  if (using_ != NULL)
  {
    x->setUsing(std::dynamic_pointer_cast<SQLTableSource>(using_->clone()));
  }
  x->only = only;
}

SQLObject_ptr SQLDeleteStatement::clone()
{
  SQLObject_ptr clone_tmp = SQLDeleteStatement_ptr(new SQLDeleteStatement());
  SQLDeleteStatement_ptr x = std::dynamic_pointer_cast<SQLDeleteStatement>(clone_tmp);
  // SQLDeleteStatement *x = new SQLDeleteStatement();
  cloneTo(x);
  return x;
}

SQLTableSource_ptr SQLDeleteStatement::getTableSource()
{
  return tableSource;
}

SQLExprTableSource_ptr SQLDeleteStatement::getExprTableSource()
{
  return std::dynamic_pointer_cast<SQLExprTableSource>(getTableSource());
}

void SQLDeleteStatement::setTableSource(SQLExpr_ptr expr)
{
  this->setTableSource(SQLExprTableSource_ptr(new SQLExprTableSource(expr)));
}

void SQLDeleteStatement::setTableSource(SQLTableSource_ptr tableSource)
{
  if (tableSource != NULL)
  {
    tableSource->setParent(SharedObject(SQLDeleteStatement));
  }
  this->tableSource = tableSource;
}

SQLName_ptr SQLDeleteStatement::getTableName()
{
  if (instanceof <SQLTableSource, SQLExprTableSource>(this->tableSource))
  {
    SQLExprTableSource_ptr exprTableSource = std::dynamic_pointer_cast<SQLExprTableSource>(this->tableSource);
    return std::dynamic_pointer_cast<SQLName>(exprTableSource->getExpr());
  }

  if (instanceof <SQLTableSource, SQLSubqueryTableSource>(tableSource))
  {
    SQLSelectQuery_ptr selectQuery = (std::dynamic_pointer_cast<SQLSubqueryTableSource>(tableSource)->getSelect()->getQuery());
    if (instanceof <SQLSelectQuery, SQLSelectQueryBlock>(selectQuery))
    {
      SQLTableSource_ptr subQueryTableSource = (std::dynamic_pointer_cast<SQLSelectQueryBlock>(selectQuery)->getFrom());
      if (instanceof <SQLTableSource, SQLExprTableSource>(subQueryTableSource))
      {
        SQLExpr_ptr subQueryTableSourceExpr = std::dynamic_pointer_cast<SQLExprTableSource>(subQueryTableSource)->getExpr();
        return std::dynamic_pointer_cast<SQLName>(subQueryTableSourceExpr);
      }
    }
  }

  return NULL;
}

void SQLDeleteStatement::setTableName(SQLName_ptr tableName)
{
  this->setTableSource(SQLExprTableSource_ptr(new SQLExprTableSource(tableName)));
}

void SQLDeleteStatement::setTableName(string_ptr name)
{
  setTableName(SQLIdentifierExpr_ptr(new SQLIdentifierExpr((name))));
}

SQLExpr_ptr SQLDeleteStatement::getWhere()
{
  return where;
}

void SQLDeleteStatement::setWhere(SQLExpr_ptr where)
{
  if (where != NULL)
  {
    where->setParent(SharedObject(SQLDeleteStatement));
  }
  this->where = where;
}

string_ptr SQLDeleteStatement::getAlias()
{
  return this->tableSource->getAlias();
}

void SQLDeleteStatement::setAlias(string_ptr alias)
{
  this->tableSource->setAlias(alias);
}

void SQLDeleteStatement::accept0(SQLASTVisitor_ptr visitor)
{
  if (visitor->visit(SharedObject(SQLDeleteStatement)))
  {
    if (with != NULL)
    {
      with->accept(visitor);
    }

    if (tableSource != NULL)
    {
      tableSource->accept(visitor);
    }

    if (where != NULL)
    {
      where->accept(visitor);
    }
  }

  visitor->endVisit(SharedObject(SQLDeleteStatement));
}

SQLObject_list_ptr SQLDeleteStatement::getChildren()
{
  SQLObject_list_ptr children = std::make_shared<std::list<SQLObject_ptr>>();
  if (with != NULL)
  {
    children->push_back(with);
  }
  children->push_back(tableSource);
  if (where != NULL)
  {
    children->push_back(where);
  }
  return children;
}

SQLTableSource_ptr SQLDeleteStatement::getFrom()
{
  return from;
}

void SQLDeleteStatement::setFrom(SQLTableSource_ptr from)
{
  if (from != NULL)
  {
    from->setParent(SharedObject(SQLDeleteStatement));
  }
  this->from = from;
}

BOOL_ptr SQLDeleteStatement::replace(SQLExpr_ptr expr, SQLExpr_ptr target)
{
  if (where == expr)
  {
    setWhere(target);
    return BOOL::TRUE;
  }
  return BOOL::FALSE;
}

BOOL_ptr SQLDeleteStatement::isOnly()
{
  return only;
}

void SQLDeleteStatement::setOnly(BOOL_ptr only)
{
  this->only = only;
}

SQLTableSource_ptr SQLDeleteStatement::getUsing()
{
  return using_;
}

void SQLDeleteStatement::setUsing(SQLTableSource_ptr using_)
{
  this->using_ = using_;
}

SQLWithSubqueryClause_ptr SQLDeleteStatement::getWith()
{
  return with;
}

void SQLDeleteStatement::setWith(SQLWithSubqueryClause_ptr with)
{
  if (with != NULL)
  {
    with->setParent(SharedObject(SQLDeleteStatement));
  }
  this->with = with;
}

void SQLDeleteStatement::addCondition(string_ptr conditionSql)
{
  if (conditionSql->empty() || conditionSql->length() == 0)
  {
    return;
  }

  SQLExpr_ptr condition = SQLUtils::toSQLExpr(conditionSql, dbType);
  addCondition(condition);
}

void SQLDeleteStatement::addCondition(SQLExpr_ptr expr)
{
  if (expr == NULL)
  {
    return;
  }

  this->setWhere(SQLBinaryOpExpr::and_(where, expr));
}

BOOL_ptr SQLDeleteStatement::removeCondition(string_ptr conditionSql)
{
  if (conditionSql->empty() || conditionSql->length() == 0)
  {
    return BOOL::FALSE;
  }

  SQLExpr_ptr condition = SQLUtils::toSQLExpr(conditionSql, dbType);

  return removeCondition(condition);
}

BOOL_ptr SQLDeleteStatement::removeCondition(SQLExpr_ptr condition)
{
  if (condition == NULL)
  {
    return BOOL::FALSE;
  }

  if (instanceof <SQLExpr, SQLBinaryOpExprGroup>(where))
  {
    SQLBinaryOpExprGroup_ptr group = std::dynamic_pointer_cast<SQLBinaryOpExprGroup>(where);

    int removedCount = 0;
    SQLExpr_list_ptr items = group->getItems();
    auto it_items = items->begin();
    for (int i = items->size() - 1; i >= 0; i--)
    {
      std::advance(it_items, i);
      if ((*it_items)->equals(condition))
      {
        // items.remove(i);
        if (it_items != items->end()) // 最后一个不能删除
          items->erase(it_items);     // 删除第i个元素
        removedCount++;
      }
    }
    if (items->empty())
    {
      where = NULL;
    }

    return (removedCount > 0) ? BOOL::TRUE : BOOL::FALSE;
  }

  if (instanceof <SQLExpr, SQLBinaryOpExpr>(where))
  {
    SQLBinaryOpExpr_ptr binaryOpWhere = std::dynamic_pointer_cast<SQLBinaryOpExpr>(where);
    SQLBinaryOperator_ptr operator_ = binaryOpWhere->getOperator();
    if (operator_->name->c_str() == SQLBinaryOperator::BooleanAnd.name->c_str() ||
        operator_->name->c_str() == SQLBinaryOperator::BooleanOr.name->c_str())
    {
      SQLExpr_list_ptr items = SQLBinaryOpExpr::split(binaryOpWhere);

      int removedCount = 0;
      auto it_items = items->begin();
      for (int i = items->size() - 1; i >= 0; i--)
      {
        std::advance(it_items, i);
        SQLExpr_ptr item = (*it_items);
        if (item->equals(condition))
        {
          if (SQLUtils::replaceInParent(item, NULL))
          {
            removedCount++;
          }
        }
      }

      return (removedCount > 0) ? BOOL::TRUE : BOOL::FALSE;
    }
  }

  if (condition->equals(where))
  {
    where = NULL;
    return BOOL::TRUE;
  }

  return BOOL::FALSE;
}

BOOL_ptr SQLDeleteStatement::addWhere(SQLExpr_ptr where)
{
  if (where == NULL)
  {
    return BOOL::FALSE;
  }

  this->addCondition(where);

  return BOOL::TRUE;
}
